﻿using System;
using System.Windows;
using JetBrains.Annotations;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.ViewModels;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents the <see cref="IBootstrapper" /> which used to start mvvm-application.
    /// </summary>
    public class NavigationBootstrapper<TRootViewModel> : NavigationBootstrapper
        where TRootViewModel : IViewModel
    {
        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="Bootstrapper" /> class.
        /// </summary>
        protected NavigationBootstrapper([CanBeNull]IInitializationStrategy initializationStrategy)
            : base(typeof(TRootViewModel), initializationStrategy)
        {
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="Bootstrapper" /> class.
        /// </summary>
        public NavigationBootstrapper([NotNull]IIocAdapter iocAdapter, [CanBeNull]IInitializationStrategy initializationStrategy)
            : base(typeof(TRootViewModel), iocAdapter, initializationStrategy)
        {
        }

        #endregion
    }

    /// <summary>
    ///     Represents the <see cref="IBootstrapper" /> which used to start mvvm-application.
    /// </summary>
    public partial class NavigationBootstrapper : Bootstrapper
    {
        #region Fields

        private readonly object _locker = new object();
        private IViewPageMappingItem _viewPageMappingItem;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="Bootstrapper" /> class.
        /// </summary>
        protected NavigationBootstrapper([NotNull]Type mainViewModelType, IInitializationStrategy initializationStrategy = null)
            : base(mainViewModelType, initializationStrategy ?? new InitializationStrategyExt())
        {
        }

        /// <summary>
        ///     Initializes a new instance of the <see cref="Bootstrapper" /> class.
        /// </summary>
        public NavigationBootstrapper([NotNull]Type mainViewModelType, [NotNull] IIocAdapter iocAdapter,
            IInitializationStrategy initializationStrategy = null)
            : base(mainViewModelType, iocAdapter, initializationStrategy ?? new InitializationStrategyExt())
        {
        }

        #endregion

        #region Overrides of Bootstrapper

        /// <summary>
        ///     Starts the current <see cref="IBootstrapper" />.
        /// </summary>
        public override void Start()
        {
            Application application = Application.Current;
            Should.NotBeNull(application, "Application.Current");
            
            IDisposable @lock = null;
            try
            {
                @lock = DeadLockDetector.Lock(_locker);
                if (IsStarted) return;
                Initialize();
                GlobalSettings.SetRootIocAdapterWithSettings(IocAdapter);
                IActivationPolicy policy = GetMainViewModelActivationPolicy() ?? GlobalSettings.DefaultActivationPolicy;
                var mappingProvider = IocAdapter.Get<IViewPageMappingProvider>();
                _viewPageMappingItem = mappingProvider.FindMappingForViewModel(MainViewModelType, policy.ViewBindingName,
                    false);
                if (_viewPageMappingItem != null)
                {
                    application.StartupUri = _viewPageMappingItem.Uri;
                    application.Navigated += ApplicationFirstOnNavigated;
                    return;
                }
                var viewModelProvider = IocAdapter.Get<IViewModelProvider>();

                Should.BeViewModel(MainViewModelType, "MainViewModelType", typeof(IWindowViewModel));
                var viewModel = (IWindowViewModel)viewModelProvider.GetViewModel(MainViewModelType, policy);
#if TASKSUPPORT
                viewModel.ShowAsync(policy.ViewBindingName)
                    .ContinueWith(result => viewModel.Dispose());
#else
                viewModel.ShowAsync(policy.ViewBindingName)
                    .BeginWait(result => viewModel.Dispose());
#endif
            }
            finally
            {
                IsStarted = true;
                if (@lock != null)
                    @lock.Dispose();
            }
        }

        /// <summary>
        ///     Stops the current <see cref="IBootstrapper" />.
        /// </summary>
        public override void Stop()
        {
            DisposeNavigationProvider();
            base.Stop();
        }

        #endregion
    }
}